#include "t6963c.h"
#include "Vt100.h"
#include <inttypes.h>
#include <string.h>

#define XY(c, r) (c+r*WINDOW_WIDTH)

uint8_t posx, posy;
uint8_t sposx, sposy;
uint8_t st, sb;

uint8_t state;

char buffer[WINDOW_WIDTH*WINDOW_HEIGHT];

void pc(char c)
{
	GLCD_TextGoTo(posx, posy);
	GLCD_WriteChar(c);
}

void refresh(void)
{
	GLCD_ClearText();
	
	GLCD_TextGoTo(0,0);
	
	for(int i = 0; i<WINDOW_WIDTH*WINDOW_HEIGHT; i++)
		GLCD_WriteChar(buffer[i]);
}

void reset(void)
{
	posx = 0;
	posy = 0;
	
	sposx = 0;
	sposy = 0;
	
	st = 0;
	sb = WINDOW_HEIGHT-1;
	
	state = 0;
	
	memset(buffer, ' ', WINDOW_WIDTH*WINDOW_HEIGHT);
}

void InitVT100(void)
{
	reset();
}

void setposx(uint8_t np)
{
	posx = np;
}

void setposy(uint8_t np)
{
	posy = np;
	if(posy >= sb)
	{
		memmove(&buffer[st*WINDOW_WIDTH], &buffer[(st+1)*WINDOW_WIDTH], (sb-st)*WINDOW_WIDTH);
		memset(&buffer[XY(0,sb)], ' ', WINDOW_WIDTH);
		posy--;
		setposx(0);
		
		refresh();
	}
	else if(posy < st)
	{
		memmove(&buffer[(st+1)*WINDOW_WIDTH], &buffer[st*WINDOW_WIDTH], (sb-st)*WINDOW_WIDTH);
		memset(&buffer[XY(0,st)], ' ', WINDOW_WIDTH);
		posy++;
		setposx(0);
		
		refresh();
	}
}

void moveh(int8_t cols, int8_t dir)
{
	if(cols == 0)
		cols = 1;
	cols = cols*dir;
	
	int8_t np = posx+cols;
	
	if(np < 0)
		np = 0;
	if(np >= WINDOW_WIDTH)
		np = WINDOW_WIDTH-1;
	
	setposx(np);
}

void movev(int8_t lines, int8_t dir)
{
	if(lines == 0)
		lines = 1;
	lines = lines*dir;
	
	int8_t np = posy+lines;
	
	if(np < 0)
		np = 0;
	if(np >= WINDOW_HEIGHT)
		np = WINDOW_HEIGHT-1;
	
	setposy(np);
}

void clearline(int8_t cl)
{
	switch(cl)
	{
		case 0:
			memset(&buffer[XY(posx, posy)], ' ', WINDOW_WIDTH-posx);
			break;
		case 1:
			memset(&buffer[XY(0,posy)], ' ', posx+1);
			break;
		case 2:
			memset(&buffer[XY(0,posy)], ' ', WINDOW_WIDTH);
			break;
		default:
			return;
	}
	refresh();
}

void clearscreen(int8_t cl)
{
	switch(cl)
	{
		case 0:
			memset(&buffer[XY(0, posy)], ' ', WINDOW_WIDTH*WINDOW_HEIGHT-XY(0, posy));
			break;
		case 1:
			memset(buffer, ' ', XY(0,posy+1));
			break;
		case 2:
			memset(buffer, ' ', WINDOW_WIDTH*WINDOW_HEIGHT);
			break;
		default:
			return;
	}
	refresh();
}

uint8_t argcount;
uint16_t args[4];

void handlecommand(char c, bool bracket)
{
	if(bracket)
	{
		switch(c)
		{
			case 'r':
				if(argcount > 1)
				{
					st = args[0];
					sb = args[1];
				}
				break;
			case 'A':
				moveh(args[0],-1);
				break;
			case 'B':
				moveh(args[0],1);
				break;	
			case 'C':
				movev(args[0],-1);
				break;	
			case 'D':
				movev(args[0],1);
				break;
			case 'f':
			case 'H':
				if(argcount > 1)
				{
					setposx(args[1]);
					setposy(args[0]);
				}
				else
				{
					setposx(0);
					setposy(0);
				}
				break;
			case 'g':
				//clear some tabs
				break;
			case 'K':
				clearline(args[0]);
				break;
			case 'J':
				clearscreen(args[0]);
				break;
			default:
				break;
		}
	}
	else
	{
		switch(c)
		{
			case 'M':
				setposy(posy+1);
				break;
			case 'D':
				setposy(posy-1);
				break;
			case 'E': //Move to next line
				setposx(0);
				setposy(posy+1);
				break;
			case 'H': //Set a tab at the current column
				break;
			case 'c':
				reset();
				break;
			case '7':
				sposx = posx;
				sposy = posy;
				break;
			case '8':
				posx = sposx;
				posy = sposy;
				break;
			default:
				break;
		}
	}
}

void VT100WriteCh(char c)
{
	switch(state)
	{
		case 0:
			switch(c)
			{
				case '\e':
					state = 1;
					argcount = 0;
					break;
				case '(':
				case ')':
				case '#':
					state = 3;
					break;
				case '\n':
					setposx(0);
					setposy(posy+1);
					break;
				case '\t':
					setposx((posx+8) & ~7);
					break;
				case 0x7:
				case 0xf:
				case '\r':
					break;
				case 0x08:
					if(posx > 0)
						setposx(posx-1);
					break;
				default:
					pc(c);
			}
			break;
		case 1: //^e
			switch(c)
			{
				case '[':
					state = 2;
					args[0] = 0;
					break;
				case '5':
				case '6':
					args[0] = (c-'0');
					argcount++;
					break;
				default:
					handlecommand(c, false);
					state = 0;
					break;
			}
			break;
		case 2: //[
			switch(c)
			{
				case ';':
					argcount++;
					args[argcount] = 0;
					break;
				case '0':
				case '1':
				case '2':
				case '3':
				case '4':
				case '5':
				case '6':
				case '7':
				case '8':
				case '9':
					args[argcount] = args[argcount]*10+(c-'0');
					break;
				default:
					handlecommand(c, true);
					state = 0;
					break;
			}
			break;
		case 3: //(/)/#
			state = 0;
			break;
	}
}
